/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.io.dot;
/* ==========================================
 * JGraphT : a free Java graph-theory library
 * ==========================================
 *
 * Project Info:  http://jgrapht.sourceforge.net/
 * Project Creator:  Barak Naveh (http://sourceforge.net/users/barak_naveh)
 *
 * (C) Copyright 2003-2007, by Barak Naveh and Contributors.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */
/* ------------------
 * DOTExporter.java
 * ------------------
 * (C) Copyright 2006, by Trevor Harmon.
 *
 * Original Author:  Trevor Harmon <trevor@vocaro.com>
 *
 */

import java.io.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.jgrapht.*;
import org.jgrapht.ext.EdgeNameProvider;
import org.jgrapht.ext.IntegerNameProvider;
import org.jgrapht.ext.VertexNameProvider;


/**
 * Exports a graph into a DOT file.
 *
 * <p>For a description of the format see <a
 * href="http://en.wikipedia.org/wiki/DOT_language">
 * http://en.wikipedia.org/wiki/DOT_language</a>.</p>
 *
 * @author Trevor Harmon
 */
public class DOTColorWithSubgraphExporter<V, E>
{
	//~ Instance fields --------------------------------------------------------

	private VertexNameProvider<V> vertexIDProvider;
	private VertexNameProvider<V> vertexLabelProvider;
	private EdgeNameProvider<E> edgeLabelProvider;
	private String orientation = "TD;";

	//~ Constructors -----------------------------------------------------------

	/**
	 * Constructs a new DOTExporter object with an integer name provider for the
	 * vertex IDs and null providers for the vertex and edge labels.
	 */
	public DOTColorWithSubgraphExporter()
	{
		this(new IntegerNameProvider<V>(), null, null);
	}

	/**
	 * Constructs a new DOTExporter object with the given ID and label
	 * providers.
	 *
	 * @param vertexIDProvider for generating vertex IDs. Must not be null.
	 * @param vertexLabelProvider for generating vertex labels. If null, vertex
	 * labels will not be written to the file.
	 * @param edgeLabelProvider for generating edge labels. If null, edge labels
	 * will not be written to the file.
	 */
	public DOTColorWithSubgraphExporter(
			VertexNameProvider<V> vertexIDProvider,
			VertexNameProvider<V> vertexLabelProvider,
			EdgeNameProvider<E> edgeLabelProvider)
	{
		this.vertexIDProvider = vertexIDProvider;
		this.vertexLabelProvider = vertexLabelProvider;
		this.edgeLabelProvider = edgeLabelProvider;
	}

	//~ Methods ----------------------------------------------------------------

	public void setRankDir(String dir)
	{
		orientation = dir;
	}


	/**
	 * Exports a graph into a plain text file in DOT format.
	 *
	 * @param writer the writer to which the graph to be exported
	 * @param g the graph to be exported
	 * @param subgraph the association of vertices with the subgraph id
	 */
	public void export(Writer writer, Graph<V, E> g, Map<V, String> subgraphNodes)
	{
		PrintWriter out = new PrintWriter(writer);
		String indent = "  ";
		String connector;

		if (g instanceof DirectedGraph<?,?>) {
			out.println("digraph G {\nrankdir=" + orientation);
			connector = " -> ";
		} else {
			out.println("graph G {");
			connector = " -- ";
		}

		Map<String,Set<V>> subG = new HashMap<String, Set<V>>();
		for (V v : g.vertexSet()) {
			String id = subgraphNodes.get(v);
			//System.err.println("subgraph "+id);
			if (id != null)
			{
				Set<V> temp = subG.get(id);
				if(temp == null)
				{
					temp = new HashSet<V>();
					subG.put(id, temp);
				}
				temp.add(v);
			}
		}
		
		int cluster_nb = 0;
		for (Entry<String, Set<V>> entry : subG.entrySet())
		{
			out.print(indent + "subgraph cluster_"+cluster_nb+" {\n");
			//System.err.println("subgraph "+entry.getKey());
			for (V v : entry.getValue())
			{
				out.print(indent + getVertexID(v));
				if (vertexLabelProvider != null) {
					out.print(
							" [label = " + vertexLabelProvider.getVertexName(v)
							+ "]");
				}
				out.println(";");
			}
			out.print(indent+ "label=" + entry.getKey() + ";\n"+ indent + "color=blue;\n"+ indent+"}\n");
			cluster_nb++;
		}

		for (V v : g.vertexSet()) {
			if (!subgraphNodes.containsKey(v))
			{
				out.print(indent + getVertexID(v));

				if (vertexLabelProvider != null) {
					out.print(
							" [label = " + vertexLabelProvider.getVertexName(v)
							+ "]");
				}

				out.println(";");
			}
		}

		for (E e : g.edgeSet()) {
			String source = getVertexID(g.getEdgeSource(e));
			String target = getVertexID(g.getEdgeTarget(e));

			out.print(indent + source + connector + target);

			if (edgeLabelProvider != null) {
				out.print(
						" [label =" + edgeLabelProvider.getEdgeName(e) + "]");
			}

			out.println(";");
		}

		out.println("}");

		out.flush();
	}

	/**
	 * Return a valid vertex ID (with respect to the .dot language definition as
	 * described in http://www.graphviz.org/doc/info/lang.html Quoted from above
	 * mentioned source: An ID is valid if it meets one of the following
	 * criteria:
	 *
	 * <ul>
	 * <li>any string of alphabetic characters, underscores or digits, not
	 * beginning with a digit;
	 * <li>a number [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? );
	 * <li>any double-quoted string ("...") possibly containing escaped quotes
	 * (\");
	 * <li>an HTML string (<...>).
	 * </ul>
	 *
	 * @throws RuntimeException if the given <code>vertexIDProvider</code>
	 * didn't generate a valid vertex ID.
	 */
	private String getVertexID(V v)
	{
		// TODO jvs 28-Jun-2008:  possible optimizations here are
		// (a) only validate once per vertex
		// (b) compile regex patterns

		// use the associated id provider for an ID of the given vertex
		String idCandidate = vertexIDProvider.getVertexName(v);

		// now test that this is a valid ID
		boolean isAlphaDig = idCandidate.matches("[a-zA-Z]+([\\w_]*)?");
		boolean isDoubleQuoted = idCandidate.matches("\".*\"");
		boolean isDotNumber =
			idCandidate.matches("[-]?([.][0-9]+|[0-9]+([.][0-9]*)?)");
		boolean isHTML = idCandidate.matches("<.*>");

		if (isAlphaDig || isDotNumber || isDoubleQuoted || isHTML) {
			return idCandidate;
		}

		throw new RuntimeException(
				"Generated id '" + idCandidate + "'for vertex '" + v
				+ "' is not valid with respect to the .dot language");
	}
}

// End DOTExporter.java

